package com.mai4j.maiadmin.misc;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.mai4j.maiadmin.model.UserPrincipal;
import com.mai4j.maiadmin.model.dto.*;
import com.mai4j.maiadmin.model.http.*;
import com.mai4j.maiadmin.model.http.response.LanguageListResp;
import com.mai4j.maiadmin.model.vo.*;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 微信公众号请关注: 开源螺丝钉
 * <br>
 * 码云请关注: <a href="https://gitee.com/xiyoufang">https://gitee.com/xiyoufang</a>
 * <br>
 * 哔哩哔哩请关注: <a href="https://space.bilibili.com/438927834">https://space.bilibili.com/438927834</a>
 *
 * @author xiyoufang
 */
@Mapper
public interface StructMapper {

    StructMapper INSTANCE = Mappers.getMapper(StructMapper.class);

    SignInDTO signInReqToSignInDTO(SignInReq signInReq);

    UserPrincipal userInfoDTOToUserPrincipal(UserInfoDTO userInfoDTO);

    List<UserVO> userDTOToUserVO(List<UserDTO> list);

    List<UserGroupVO> userGroupDTOToUserGroupVO(List<UserGroupDTO> list);

    // # Language
    LanguageVO languageDTOToLanguageVO(LanguageDTO languages);

    LanguageListResp.Language languageDTOToLanguageListRespLanguage(LanguageDTO languageDTO);

    @Mapping(target = "dateModified", expression = "java(null)")
    @Mapping(target = "dateAdded", expression = "java(null)")
    LanguageDTO languageSaveOrUpdateReqToLanguageDTO(LanguageSaveOrUpdateReq languageSaveOrUpdateReq);


    // # AdminMenu
    @Mapping(target = "descriptions", expression = "java(toAdminMenuVOAdminMenuDescriptions(adminMenuDTO.getDescriptions()))")
    @Mapping(target = "content", expression = "java(toAdminMenuContent(adminMenuDTO.getContent()))")
    AdminMenuVO adminMenuDTOToAdminMenuVO(AdminMenuDTO adminMenuDTO);

    default Map<String, AdminMenuVO.AdminMenuDescription> toAdminMenuVOAdminMenuDescriptions(List<AdminMenuDescriptionDTO> descriptionDTOS) {
        return descriptionDTOS.stream()
                .map(d -> new AdminMenuVO.AdminMenuDescription()
                        .setName(d.getName())
                        .setLanguageCode(d.getLanguageCode()))
                .collect(Collectors.toMap(AdminMenuVO.AdminMenuDescription::getLanguageCode, (a) -> a, (a1, a2) -> a1));
    }

    default List<AdminMenuVO.AdminMenuItem> toAdminMenuContent(String content) {
        return new Gson().fromJson(content,
                new TypeToken<List<AdminMenuVO.AdminMenuItem>>() {
                }.getType());
    }

    @Mapping(target = "dateModified", expression = "java(null)")
    @Mapping(target = "dateAdded", expression = "java(null)")
    @Mapping(target = "descriptions", expression = "java(adminMenuSaveOrUpdateReqAdminMenuDescriptionsToAdminMenuDescriptionDTOs(adminMenuSaveReq.getDescriptions()))")
    @Mapping(target = "content", expression = "java(adminMenuItemReqContentToAdminMenuContent(adminMenuSaveReq.getContent()))")
    AdminMenuDTO adminMenuSaveOrUpdateReqToAdminMenuDTO(AdminMenuSaveOrUpdateReq adminMenuSaveReq);

    default List<AdminMenuDescriptionDTO> adminMenuSaveOrUpdateReqAdminMenuDescriptionsToAdminMenuDescriptionDTOs(Map<String, AdminMenuSaveOrUpdateReq.AdminMenuDescription> descriptions) {
        List<AdminMenuDescriptionDTO> descriptionDTOS = new ArrayList<>();
        for (Map.Entry<String, AdminMenuSaveOrUpdateReq.AdminMenuDescription> entry : descriptions.entrySet()) {
            descriptionDTOS.add(new AdminMenuDescriptionDTO()
                    .setLanguageCode(entry.getKey())
                    .setName(entry.getValue().getName()));
        }
        return descriptionDTOS;
    }

    default String adminMenuItemReqContentToAdminMenuContent(List<AdminMenuItemReq> adminMenuItemReqs) {
        return new GsonBuilder().setPrettyPrinting().create().toJson(adminMenuItemReqs);
    }

    // # Location
    @Mapping(target = "dateModified", expression = "java(null)")
    @Mapping(target = "dateAdded", expression = "java(null)")
    @Mapping(target = "descriptions", expression = "java(locationSaveOrUpdateReqLocationDescriptionsToLocationDescriptionDTOs(locationSaveOrUpdateReq.getDescriptions()))")
    LocationDTO locationSaveOrUpdateReqToLocationDTO(LocationSaveOrUpdateReq locationSaveOrUpdateReq);

    default List<LocationDescriptionDTO> toLocationDescriptionDTOs(Map<String, String> name) {
        List<LocationDescriptionDTO> descriptionDTOS = new ArrayList<>();
        for (Map.Entry<String, String> entry : name.entrySet()) {
            descriptionDTOS.add(new LocationDescriptionDTO().setLanguageCode(entry.getKey()).setName(entry.getValue()));
        }
        return descriptionDTOS;
    }

    default List<LocationDescriptionDTO> locationSaveOrUpdateReqLocationDescriptionsToLocationDescriptionDTOs(Map<String, LocationSaveOrUpdateReq.LocationDescription> descriptions) {
        List<LocationDescriptionDTO> descriptionDTOS = new ArrayList<>();
        for (Map.Entry<String, LocationSaveOrUpdateReq.LocationDescription> entry : descriptions.entrySet()) {
            descriptionDTOS.add(new LocationDescriptionDTO()
                    .setLanguageCode(entry.getKey())
                    .setName(entry.getValue().getName()));
        }
        return descriptionDTOS;
    }

    @Mapping(target = "descriptions", expression = "java(toLocationVOLocationDescriptions(locationDTO.getDescriptions()))")
    LocationVO locationDTOToLocationVO(LocationDTO locationDTO);

    default Map<String, LocationVO.LocationDescription> toLocationVOLocationDescriptions(List<LocationDescriptionDTO> descriptionDTOS) {
        return descriptionDTOS.stream()
                .map(d -> new LocationVO.LocationDescription()
                        .setName(d.getName())
                        .setLanguageCode(d.getLanguageCode()))
                .collect(Collectors.toMap(LocationVO.LocationDescription::getLanguageCode, (a) -> a, (a1, a2) -> a1));
    }

    // # Currency
    @Mapping(target = "descriptions", expression = "java(toCurrencyVOCurrencyDescriptions(currencyDTO.getDescriptions()))")
    CurrencyVO currencyDTOToCurrencyVO(CurrencyDTO currencyDTO);

    default Map<String, CurrencyVO.CurrencyDescription> toCurrencyVOCurrencyDescriptions(List<CurrencyDescriptionDTO> descriptionDTOS) {
        return descriptionDTOS.stream()
                .map(d -> new CurrencyVO.CurrencyDescription()
                        .setName(d.getName())
                        .setLanguageCode(d.getLanguageCode()))
                .collect(Collectors.toMap(CurrencyVO.CurrencyDescription::getLanguageCode, (a) -> a, (a1, a2) -> a1));
    }

    @Mapping(target = "dateModified", expression = "java(null)")
    @Mapping(target = "dateAdded", expression = "java(null)")
    @Mapping(target = "descriptions", expression = "java(currencySaveOrUpdateReqCurrencyDescriptionsToCurrencyDescriptionDTOs(currencySaveOrUpdateReq.getDescriptions()))")
    CurrencyDTO currencySaveOrUpdateReqToCurrencyDTO(CurrencySaveOrUpdateReq currencySaveOrUpdateReq);

    default List<CurrencyDescriptionDTO> currencySaveOrUpdateReqCurrencyDescriptionsToCurrencyDescriptionDTOs(Map<String, CurrencySaveOrUpdateReq.CurrencyDescription> descriptions) {
        List<CurrencyDescriptionDTO> descriptionDTOS = new ArrayList<>();
        for (Map.Entry<String, CurrencySaveOrUpdateReq.CurrencyDescription> entry : descriptions.entrySet()) {
            descriptionDTOS.add(new CurrencyDescriptionDTO()
                    .setLanguageCode(entry.getKey())
                    .setName(entry.getValue().getName()));
        }
        return descriptionDTOS;
    }

    // # SqlHistory
    SqlHistoryVO sqlHistoryDTOToSqlHistoryVO(SqlHistoryDTO sqlHistoryDTO);
}
